/********************************************************************
	created:	30:9:2007   21:13
	author:		tiamo
	purpose:	read
*********************************************************************/

#include "stdafx.h"

// insert codes
BOOLEAN AppleKeyboardInsertCodesIntoQueue(__in PVOID pContext,__in PCHAR pNewScanCodes,__in ULONG ulLength)
{
	if(ulLength <= 0)
		return TRUE;

	CDeviceExtension* pDevExt = static_cast<CDeviceExtension*>(pContext);
	PSERVICE_CALLBACK_ROUTINE fnClassService = static_cast<PSERVICE_CALLBACK_ROUTINE>(pDevExt->m_dataConnect.ClassService);

	PKEYBOARD_INPUT_DATA pInputData = &pDevExt->m_dataKeyboardInput;

	ULONG i = 0;
	PUCHAR pBuffer = static_cast<PUCHAR>(static_cast<PVOID>(pNewScanCodes));
	for(ULONG i = 0; i < ulLength; i ++)
	{
		UCHAR code = pBuffer[i];
		if(code == 0xff)
		{
			AppleDebugPrint(DBGLEVEL_WARNING,"AppleKeyboard overrun\n");
			pDevExt->m_ulScanCodesState = 0;
			pInputData->MakeCode = 0xff;
			pInputData->Flags = 0;
		}
		else
		{
			if(!pDevExt->m_ulScanCodesState)
			{
				if(code == 0xe0)
				{
					pInputData->Flags |= KEY_E0;
					pDevExt->m_ulScanCodesState = KEY_E0;
					continue;
				}
				else if(code == 0xe1)
				{
					pInputData->Flags |= KEY_E1;
					pDevExt->m_ulScanCodesState = KEY_E1;
					continue;
				}
			}
					
			if(code > 0x7f)
			{
				code = code & 0x7f;
				pInputData->Flags |= KEY_BREAK;
			}

			pInputData->MakeCode = code;
			pDevExt->m_ulScanCodesState = 0;

			if(pDevExt->m_lEnableCount)
			{
				KIRQL saveIrql;
				KeRaiseIrql(DISPATCH_LEVEL,&saveIrql);

				ULONG ulCount = 1;
				fnClassService(pDevExt->m_dataConnect.ClassDeviceObject,pInputData,pInputData + 1,&ulCount);
				KeLowerIrql(saveIrql);
			}

			pInputData->Flags = 0;
		}
	}

	return TRUE;
}

// auto repeat
VOID AppleKeyboardAutoRepeat(__in PKDPC pDpc,__in PVOID pDeferredContext,__in PVOID pSystemArgument1,__in PVOID pSystemArgument2)
{
	CDeviceExtension* pDevExt = static_cast<CDeviceExtension*>(pDeferredContext);
	CHidData* pHid = pDevExt->m_pHidData;

	HidP_TranslateUsageAndPagesToI8042ScanCodes(pHid->m_pChangedMakeUsageList,pHid->m_ulMaxUsageListLength,HidP_Keyboard_Make,&pHid->m_stateModifier,
												&AppleKeyboardInsertCodesIntoQueue,pDevExt);
}

// initiate start read
VOID AppleKeyboardInitiateStartRead(__in PKDPC pDpc,__in PVOID pDeferredContext,__in PVOID pSystemArgument1,__in PVOID pSystemArgument2)
{
	CDeviceExtension* pDevExt = static_cast<CDeviceExtension*>(pDeferredContext);
	IoAcquireRemoveLock(&pDevExt->m_removeLock,pDevExt->m_pReadIrp);
	AppleKeyboardStartRead(pDevExt);
}

// read complete
NTSTATUS AppleKeyboardReadComplete(__in PDEVICE_OBJECT pDeviceObject,__in PIRP pIrp,__in PVOID pContext)
{
	CDeviceExtension* pDevExt = static_cast<CDeviceExtension*>(pContext);
	CHidData* pHid = pDevExt->m_pHidData;

	BOOLEAN bShouldLoopIrpInThisFunction = FALSE;
	if(InterlockedCompareExchange(&pDevExt->m_lReadState,READ_STATE_LOOPING,READ_STATE_BEGIN) != READ_STATE_BEGIN)
		bShouldLoopIrpInThisFunction = TRUE;

	NTSTATUS status = pIrp->IoStatus.Status;

	if( !pDevExt->m_lEnableCount || status == STATUS_DELETE_PENDING || status == STATUS_PRIVILEGE_NOT_HELD ||
		status == STATUS_DEVICE_NOT_CONNECTED || status == STATUS_CANCELLED)
	{
		if(bShouldLoopIrpInThisFunction)
		{
			KeSetEvent(&pDevExt->m_eventPingPongStopped,IO_NO_INCREMENT,FALSE);
			IoReleaseRemoveLock(&pDevExt->m_removeLock,pIrp);
			bShouldLoopIrpInThisFunction = FALSE;
		}
	}
	else
	{
		if(!NT_SUCCESS(status))
		{
			AppleDbgBreakPoint();
		}
		else
		{
			ASSERT(pIrp->IoStatus.Information == pHid->m_hidCaps.InputReportByteLength);

			ULONG ulLength = pHid->m_ulMaxUsageListLength;
			HidP_GetUsagesEx(HidP_Input,HIDP_LINK_COLLECTION_UNSPECIFIED,pHid->m_pThisInputUsageList,&ulLength,
							 pHid->m_pPreparsedData,static_cast<PCHAR>(pHid->m_pInputReportBuffer),pHid->m_hidCaps.InputReportByteLength);

			// press printscreen and f13 will break into debugger
			if( KdDebuggerEnabled && ulLength == 1 && pHid->m_pThisInputUsageList->UsagePage == HID_USAGE_PAGE_KEYBOARD && 
				(pHid->m_pThisInputUsageList->Usage == HID_USAGE_KEYBOARD_PRINT_SCREEN || pHid->m_pThisInputUsageList->Usage == 0x69))
			{
				DbgBreakPointWithStatus(DBG_STATUS_SYSRQ);
			}

			BOOLEAN bStopProcess = FALSE;
			for(ULONG i = 0; i < pHid->m_hidCaps.InputReportByteLength; i ++)
			{
				AppleKeyboardMapUsage(pDevExt,pHid->m_pThisInputUsageList + i);
				
				if(pHid->m_pThisInputUsageList[i].UsagePage == HID_USAGE_PAGE_KEYBOARD)
				{
					if(pHid->m_pThisInputUsageList[i].Usage == HID_USAGE_KEYBOARD_ROLLOVER)
					{
						bStopProcess = TRUE;
						break;
					}

					if(pHid->m_pThisInputUsageList[i].Usage == HID_USAGE_KEYBOARD_NOEVENT)
						break;
				}
			}

			if(!bStopProcess)
			{
				// compare Prev and Current
				HidP_UsageAndPageListDifference(pHid->m_pPrevInputUsageList,pHid->m_pThisInputUsageList,pHid->m_pBreakUsageList,
												pHid->m_pMakeUsageList,pHid->m_ulMaxUsageListLength);

			#define  NO_EVENT(p) ((p)->Usage == HID_USAGE_KEYBOARD_NOEVENT && (p)->UsagePage == HID_USAGE_PAGE_UNDEFINED)
			#define  HAS_EVENT(p) ((p)->Usage != HID_USAGE_KEYBOARD_NOEVENT || (p)->UsagePage != HID_USAGE_PAGE_UNDEFINED)

				// some key has been pressed or released
				if(HAS_EVENT(pHid->m_pMakeUsageList) || HAS_EVENT(pHid->m_pBreakUsageList))
				{
					RtlCopyMemory(pHid->m_pMakeUsageList,pHid->m_pChangedMakeUsageList,pHid->m_ulMaxUsageListLength * sizeof(USAGE_AND_PAGE));

					HidP_UsageAndPageListDifference(pHid->m_pPrevInputUsageList,pHid->m_pThisInputUsageList,pHid->m_pChangedBreakUsageList,
													pHid->m_pChangedMakeUsageList,pHid->m_ulMaxUsageListLength);

					AppleKeyboardTranslateUsageAndPagesToI8042ScanCodes(pHid->m_pChangedBreakUsageList,pHid->m_ulMaxUsageListLength,HidP_Keyboard_Break,
																		&pHid->m_stateModifier,&AppleKeyboardInsertCodesIntoQueue,pDevExt);

					if(NO_EVENT(pHid->m_pThisInputUsageList) || HAS_EVENT(pHid->m_pChangedMakeUsageList))
					{
						AppleKeyboardTranslateUsageAndPagesToI8042ScanCodes(pHid->m_pChangedMakeUsageList,pHid->m_ulMaxUsageListLength,HidP_Keyboard_Make,
																			&pHid->m_stateModifier,&AppleKeyboardInsertCodesIntoQueue,pDevExt);
					}
					else
					{
						// first press a,repeat a
						// then holding a and press b,repeat b
						// then rlease a,changed make should be b
						HidP_UsageAndPageListDifference(pHid->m_pChangedBreakUsageList,pHid->m_pMakeUsageList,pHid->m_pBreakUsageList,
														pHid->m_pChangedMakeUsageList,pHid->m_ulMaxUsageListLength);
					}

					// swap pointer
					PUSAGE_AND_PAGE pTemp = pHid->m_pPrevInputUsageList;
					pHid->m_pPrevInputUsageList = pHid->m_pThisInputUsageList;
					pHid->m_pThisInputUsageList = pTemp;

					if(ulLength > 0)
					{
						if( pHid->m_pChangedBreakUsageList->Usage == HID_USAGE_KEYBOARD_NOEVENT && 
							pHid->m_pChangedMakeUsageList->Usage == HID_USAGE_KEYBOARD_NOEVENT)
						{
							AppleDbgBreakPoint();

							if(!pDevExt->m_bKeyboardProblem)
							{
								pDevExt->m_bKeyboardProblem = TRUE;
								AppleDebugPrint(DBGLEVEL_WARNING,"AppleKeyboard is sending useless reports\n");
								pDevExt->m_ulProblemFlags |= 1;
								AppleUpdateRegistryProblemFlags(pDevExt,pHid->m_pThisInputUsageList);
							}

							if(!pDevExt->m_bDeletePending && bShouldLoopIrpInThisFunction)
								KeSetTimerEx(&pDevExt->m_timerRecover,pDevExt->m_llTimeRecover,0,&pDevExt->m_dpcInitiateStartRead);

							IoReleaseRemoveLock(&pDevExt->m_removeLock,pIrp);
							KeCancelTimer(&pDevExt->m_timerRepeat);
							return STATUS_MORE_PROCESSING_REQUIRED;
						}
						else
						{
							KeSetTimerEx(&pDevExt->m_timerRepeat,pDevExt->m_llTimeRepeatDelay,pDevExt->m_lRepeatPeriod,&pDevExt->m_dpcAutoRepeat);
							// jmp 11ce4
						}
					}
					else
					{
						KeCancelTimer(&pDevExt->m_timerRepeat);
						// jmp 11ce4
					}
				}
			}
		}
	}

	// 11CE4
	if(bShouldLoopIrpInThisFunction)
		AppleKeyboardStartRead(pDevExt);

	// 11C5F
	return STATUS_MORE_PROCESSING_REQUIRED;
}

// start read
NTSTATUS AppleKeyboardStartRead(__in CDeviceExtension* pDevExt)
{
	CHidData* pHid = pDevExt->m_pHidData;
	NTSTATUS statusIrp = pDevExt->m_pReadIrp->IoStatus.Status;
	PIRP pIrp = pDevExt->m_pReadIrp;

	for(;;)
	{
		LONG lOldValue = InterlockedExchange(&pDevExt->m_lReadState,READ_STATE_BEGIN);
		ASSERT(lOldValue == READ_STATE_END);

		if(NT_SUCCESS(statusIrp))
		{
			IoReuseIrp(pIrp,STATUS_SUCCESS);
			pIrp->MdlAddress = pHid->m_mdlInputReportBuffer;
			PIO_STACK_LOCATION pNextIrpSp = IoGetNextIrpStackLocation(pIrp);

			pNextIrpSp->MajorFunction = IRP_MJ_READ;
			pNextIrpSp->Parameters.Read.Length = pHid->m_hidCaps.InputReportByteLength;
			pNextIrpSp->Parameters.Read.Key = 0;
			pNextIrpSp->Parameters.Read.ByteOffset.QuadPart = 0;
			pNextIrpSp->FileObject = pDevExt->m_pReadFileObject;
			IoSetCompletionRoutine(pIrp,&AppleKeyboardReadComplete,pDevExt,TRUE,TRUE,TRUE);

			KeResetEvent(&pDevExt->m_eventReadComplete);

			if(pDevExt->m_lEnableCount && !pDevExt->m_bDeletePending)
			{
				ASSERT(pDevExt->m_pReadFileObject);
				statusIrp = IoCallDriver(pDevExt->m_pLowerDevice,pIrp);
				KeSetEvent(&pDevExt->m_eventReadComplete,IO_NO_INCREMENT,FALSE);

				lOldValue = InterlockedExchange(&pDevExt->m_lReadState,READ_STATE_END);

				if(lOldValue == READ_STATE_LOOPING)
				{
					AppleDebugPrint(DBGLEVEL_NONE,"AppleKeyboard will looping read\n");
					continue;
				}

				AppleDebugPrint(DBGLEVEL_NONE,"AppleKeyboard Read is pending\n");
				break;
			}

			IoReleaseRemoveLock(&pDevExt->m_removeLock,pIrp);
			statusIrp = pDevExt->m_bDeletePending ? STATUS_DELETE_PENDING : STATUS_UNSUCCESSFUL;
			KeSetEvent(&pDevExt->m_eventReadComplete,IO_NO_INCREMENT,FALSE);
			KeSetEvent(&pDevExt->m_eventPingPongStopped,IO_NO_INCREMENT,FALSE);
			break;
		}

		if( statusIrp == STATUS_PRIVILEGE_NOT_HELD || statusIrp == STATUS_CANCELLED || 
			statusIrp == STATUS_DELETE_PENDING || statusIrp == STATUS_DEVICE_NOT_CONNECTED)
		{
			KeSetEvent(&pDevExt->m_eventPingPongStopped,IO_NO_INCREMENT,FALSE);
			IoReleaseRemoveLock(&pDevExt->m_removeLock,pIrp);
			break;
		}
		
		AppleDbgBreakPoint();
	}	

	return statusIrp;
}

/*
	about read state
	1.before read,we set the state = START
	2.then call the lower driver
	3.IoCallDriver will return
	4.if the Complete Routine has not been called,the state will remain START,we change the state to END,and stop read loop
	  when the Complete Routine enter,it check the state,if the state is END,so the read function is terminated,
	  and the Complete Routine will call the read function
    5.if the Complete Routine has been called,the Complete Routine see the state is START,then the Complete Routine will set the state to LOOPING
	  and do not call the read function,so after IoCallDriver return,the read function see the state is LOOPING,it will loop to send read IRP
*/